~(function(root){
  const flow = {
    $nodeWidth: 46,
    $nodeHeight: 46,
    $lineToolWidth: 80,
    $lineToolHeight: 20,
    $minDotLen: 20,
    $nodeDotSize: 12,
    $startId: 100,
    $watermark: 'Smtflow.com'
  }
  root.ondragstart = function() {return false;}
  root.oncontextmenu = function() {return false;};
  if (typeof exports === 'object'){
      if (module !== undefined && module.exports) {        
          this.apply(flow, arguments);
          exports = module.exports = flow;
      }
  }
}).call(function($w){
  const $fn = {
    ajax: function (a) {
    	let request = new XMLHttpRequest();
    	request.open(typeof(a.type) == "undefined" ? 'get' : a.type, a.url);
    	request.send(null);
    	request.onload = function() {
    		if(request.status == 200) {
    			let responseText = request.responseText;
    			let data = typeof(a.dataType) == 'undefined' || a.dataType == 'json' ? JSON.parse(responseText) : responseText;
    			if(a.success)a.success(data);
    		}
    	}
    }
  }
  let This = this;
  This.$get = (a) => {a.type = 'get';$fn.ajax(a);}

  This.$getId = type => (type || 'X') + Number(Math.floor(new Date().getTime() / 1000) + '' + (This.$startId ++)).toString(36).toUpperCase();

  This.$icon = (url, fn) => {
    if(typeof fn == 'function') {
      This.$get({
        dataType: 'text',
        url: url,
        success(data) {
          let base64 = 'data:image/svg+xml;base64,' + $w.btoa(data);
          fn(base64);
        }
      })
      return url;
    }
  }
  This.$nodeXyScope = (woft, x, y) => {
    let pixel = 0.5;
    x = x + pixel;
    y = y + pixel;
    x = (x < 0 ? pixel : x);
    y = (y < 0 ? pixel : y);
    let maxX = woft.width - This.$nodeWidth - pixel;
    let maxY = woft.height - This.$nodeHeight - pixel;
    x = (x > maxX) ? maxX: x;
    y = (y > maxY) ? maxY: y;
    return {x: x, y: y};
  }
  This.$dotPos = (dotType, nodePos) => {
    let x ,y;
    if(dotType == 'top') {
      x = nodePos.x + This.$nodeWidth / 2;
      y = nodePos.y;
    }
    else if(dotType == 'right') {
      x = nodePos.x + This.$nodeWidth;
      y = nodePos.y + This.$nodeHeight / 2;
    }
    else if(dotType == 'bottom') {
      x = nodePos.x + This.$nodeWidth / 2;
      y = nodePos.y + This.$nodeHeight;
    }
    else if(dotType == 'left') {
      x = nodePos.x;
      y = nodePos.y + This.$nodeHeight / 2;
    }
    return {x, y};
  }
  This.$lineToolPos = (e, lineType, startPos, endPos) => {
    let style = {};
    if(lineType == 'line') {											
      let left = (parseFloat(startPos.x) + parseFloat(endPos.x)) / 2 - This.$lineToolWidth / 2;
      let top = (parseFloat(startPos.y) + parseFloat(endPos.y)) / 2 - This.$lineToolHeight / 2;
      style = {left: left + 'px', top: top + 'px'};
    }
    else if(lineType == 'polyline') {
      let left = e.offsetX - (This.$lineToolWidth / 2);
      let top = e.offsetY - (This.$lineToolHeight / 2);
      style = {left: left + 'px', top: top + 'px'};
    }
    return style;
  }
  This.$getData = (nodeArr, lineArr) => {
    let nodes = nodeArr.map(node => {
      return {
        id: node.id,
        title: node.title,
        x: node.translate[0],
        y: node.translate[1],
        type: node.type
      }
    });
    let lines = lineArr.map(line => {
      return {
        id: line.id,
        points: line.points,
        dasharray: line.dasharray,
        type: line.type,
        from: line.from,
        to: line.to
      }
    })
    return {
      nodes,
      lines
    }
  }
  This.$changeLinePoints = (lines, nodeId, scope) => {
    lines.forEach(line => {
      if(line.from.id === nodeId) {
        let fromNodePos = scope;
        let fromDotPos = This.$dotPos(line.from.dot, fromNodePos);
        let toNodePos = line.to.nodePos;
        let lineParams = {
          type: line.type,
          fromDot: line.from.dot,
          fromPoints: [fromDotPos.x, fromDotPos.y],
          fromNodePos,
          toDot: line.to.dot,
          toPoints: [line.to.x, line.to.y],                
          toNodePos
        }
        line.points = This.$linePoints(lineParams);
        line.from.nodePos = fromNodePos;
        line.from.x = fromDotPos.x;
        line.from.y = fromDotPos.y
      }
      else if(line.to.id === nodeId) {
        let toNodePos = scope;
        let toDotPos = This.$dotPos(line.to.dot, toNodePos);

        let fromNodePos = line.from.nodePos;
        let lineParams = {
          type: line.type,
          fromDot: line.from.dot,
          fromPoints: [line.from.x, line.from.y],
          fromNodePos,
          toDot: line.to.dot,
          toPoints: [toDotPos.x, toDotPos.y],
          toNodePos
        }
        line.points = This.$linePoints(lineParams);
        line.to.nodePos = toNodePos;
        line.to.x = toDotPos.x;
        line.to.y = toDotPos.y
      }
    })
  }
  This.$getDataURL = (canvas, tilte, image, options) => {
    canvas.setAttribute('width', options.width);
    canvas.setAttribute('height', options.height);
    let ctx = canvas.getContext("2d");
    ctx.drawImage(image, 0, 0, options.width, options.height);
    ctx.rect(0, 0, options.width, options.height);
    ctx.strokeStyle="#CCCCCC";
    ctx.stroke();
    ctx.fillStyle="rgba(69, 69, 69, 0.9)";
    ctx.font="20px 微软雅黑";
    ctx.fillText(tilte, 50, 50);
    ctx.fillStyle="rgba(136, 136, 136, 0.7)";
    ctx.font="italic 16px Georgia";
    ctx.fillText(This.$watermark, options.width - 250, options.height -40);
    return canvas.toDataURL('image/png');
  }
  This.$reEncode = (data) => {
    return decodeURIComponent(encodeURIComponent(data).replace(/%([0-9A-F]{2})/g, (a, b) => {
          const c = String.fromCharCode(`0x${b}`);
          return c === '%' ? '%25' : c;
        }));
  }
	This.$lurbPoints = (
    minDotLen,
    fromDot, toDot, fromPoints, toPoints,
    fromX, fromY, toX, toY,
    fromTopY, fromRightX, fromBottomY, fromLeftX,
    toTopY, toBottomY, toRightX, toLeftX,
    halfX, halfY
  ) => {
    let points = [];
    if(fromDot == 'top') {
      points.push(fromX + ',' + fromTopY);
      if(toDot == 'top') {
        points.push(toX + ',' + fromTopY);
      }
      else if(toDot == 'right') {
        points.push(toRightX + ',' + fromTopY);
        points.push(toRightX + ',' + toY);
      }
      else if(toDot == 'bottom') {
        points.push(halfX + ',' + fromTopY);
        points.push(halfX + ',' + toBottomY);
        points.push(toX + ',' + toBottomY);
      }
      else if(toDot == 'left') {
        if(toLeftX < halfX) {
          points.push(toLeftX + ',' + fromTopY);
          points.push(toLeftX + ',' + toY);
        }
        else {
          points.push(halfX + ',' + fromTopY);
          points.push(halfX + ',' + toY);
        }
      }
    }
    else if(fromDot == 'right') {
      if(toDot == 'top') {
        if(toTopY < fromY) {
          if(halfX < fromRightX) {
            points.push(fromRightX + ',' + fromY);
            points.push(fromRightX + ',' + toTopY);
            points.push(toX + ',' + toTopY);
          }
          else {
            points.push(halfX + ',' + fromY);
            points.push(halfX + ',' + toTopY);
            points.push(toX + ',' + toTopY);
          }
        }
        else if(toX < fromRightX) {
          if(toTopY < halfY) {
            points.push(fromRightX + ',' + fromY);
            points.push(fromRightX + ',' + toTopY);
            points.push(toX + ',' + toTopY);
          }
          else {
            points.push(fromRightX + ',' + fromY);
            points.push(fromRightX + ',' + halfY);
            points.push(toX + ',' + halfY);
          }
        }
        else {
          points.push(toX + ',' + fromY);
        }
      }
      else if(toDot == 'right') {
        points.push(toRightX + ',' + fromY);
        points.push(toRightX + ',' + toY);
      }
      else if(toDot == 'bottom') {
        if(halfX < fromRightX) {
          points.push(fromRightX + ',' + fromY);
          points.push(fromRightX + ',' + toBottomY);
          points.push(toX + ',' + toBottomY);
        }
        else {
          points.push(halfX + ',' + fromY);
          points.push(halfX + ',' + toBottomY);
          points.push(toX + ',' + toBottomY);
        }
      }
      else if(toDot == 'left') {
        if(toLeftX < fromRightX) {
          points.push(fromRightX + ',' + fromY);
          points.push(fromRightX + ',' + halfY);
          points.push(toLeftX + ',' + halfY);
          points.push(toLeftX + ',' + toY);
        }
        else {	
          points.push(halfX + ',' + fromY);
          points.push(halfX + ',' + toY);
        }
      }
    }
    else if(fromDot == 'bottom') {
      if(toDot == 'top') {
        if(toTopY < fromBottomY) {
          points.push(fromX + ',' + fromBottomY);
          points.push(halfX + ',' + fromBottomY);
          points.push(halfX + ',' + toTopY);
          points.push(toX + ',' + toTopY);
        }
        else {
          points.push(fromX + ',' + halfY);
          points.push(toX + ',' + halfY);
        }
      }
      else if(toDot == 'right') {
        if(halfY < fromBottomY) {
          points.push(fromX + ',' + fromBottomY);
          points.push(toRightX + ',' + fromBottomY);
          points.push(toRightX + ',' + toY);
        }
        else {
          points.push(fromX + ',' + halfY);
          points.push(toRightX + ',' + halfY);
          points.push(toRightX + ',' + toY);
        }
      }
      else if(toDot == 'bottom') {
        points.push(fromX + ',' + toBottomY);
        points.push(toX + ',' + toBottomY);
      }
      else if(toDot == 'left') {
        if(toLeftX < fromX) {
          if(halfY < fromBottomY) {
            points.push(fromX + ',' + fromBottomY);
            points.push(toLeftX + ',' + fromBottomY);
            points.push(toLeftX + ',' + toY);
          }
          else {
            points.push(fromX + ',' + halfY);
            points.push(toLeftX + ',' + halfY);
            points.push(toLeftX + ',' + toY);
          }
        }
        else if(toY < fromBottomY) {
          if(toLeftX < halfX) {
            points.push(fromX + ',' + fromBottomY);
            points.push(toLeftX + ',' + fromBottomY);
            points.push(toLeftX + ',' + toY);
          }
          else {
            points.push(fromX + ',' + fromBottomY);
            points.push(halfX + ',' + fromBottomY);
            points.push(halfX + ',' + toY);
          }
        }
        else {
          points.push(fromX + ',' + toY);
        }
      }
    }
    else if(fromDot == 'left') {
      if(toDot == 'top') {
        if(toTopY < halfY) {
          points.push(fromLeftX + ',' + fromY);
          points.push(fromLeftX + ',' + toTopY);
          points.push(toX + ',' + toTopY);
        }
        else {
          points.push(fromLeftX + ',' + fromY);
          points.push(fromLeftX + ',' + halfY);
          points.push(toX + ',' + halfY);
        }
      }
      else if(toDot == 'right') {
        points.push(fromLeftX + ',' + fromY);
        points.push(fromLeftX + ',' + halfY);
        points.push(toRightX + ',' + halfY);
        points.push(toRightX + ',' + toY);
      }
      else if(toDot == 'bottom') {
        points.push(fromLeftX + ',' + fromY);
        points.push(fromLeftX + ',' + toBottomY);
        points.push(toX + ',' + toBottomY);
      }
      else if(toDot == 'left') {
        points.push(fromLeftX + ',' + fromY);
        points.push(fromLeftX + ',' + toY);
      }
    }
    return points;
  }
  This.$rulbPoints = (
    minDotLen,
    fromDot, toDot, fromPoints, toPoints,
    fromX, fromY, toX, toY,
    fromTopY, fromRightX, fromBottomY, fromLeftX,
    toTopY, toBottomY, toRightX, toLeftX,
    halfX, halfY
  ) => {
    let points = [];
    if(fromDot == 'top') {
      if(toDot == 'top') {
        points.push(fromX + ',' + toTopY);
        points.push(toX + ',' + toTopY);
      }
      else if(toDot == 'right') {
        if(fromTopY < halfY) {
          points.push(fromX + ',' + fromTopY);
          points.push(toRightX + ',' + fromTopY);
          points.push(toRightX + ',' + toY);
        }
        else {
          points.push(fromX + ',' + halfY);
          points.push(toRightX + ',' + halfY);
          points.push(toRightX + ',' + toY);
        }
      }
      else if(toDot == 'bottom') {
        if(fromTopY < toBottomY) {
          points.push(fromX + ',' + fromTopY);
          points.push(halfX + ',' + fromTopY);
          points.push(halfX + ',' + toBottomY);
          points.push(toX + ',' + toBottomY);
        }
        else {
          points.push(fromX + ',' + halfY);
          points.push(toX + ',' + halfY);
        }				
      }
      else if(toDot == 'left') {
        if(toLeftX < fromX) {
          if(fromTopY < halfY) {
            points.push(fromX + ',' + fromTopY);
            points.push(toLeftX + ',' + fromTopY);
            points.push(toLeftX + ',' + toY);
          }
          else {
            points.push(fromX + ',' + halfY);
            points.push(toLeftX + ',' + halfY);
            points.push(toLeftX + ',' + toY);
          }
        }
        else if(fromTopY < toY) {
          if(toLeftX < halfX) {
            points.push(fromX + ',' + fromTopY);
            points.push(toLeftX + ',' + fromTopY);
            points.push(toLeftX + ',' + toY);
          }
          else {
            points.push(fromX + ',' + fromTopY);
            points.push(halfX + ',' + fromTopY);
            points.push(halfX + ',' + toY);
          }
        }
        else {
          points.push(fromX + ',' + toY);
        }
      }
    }
    else if(fromDot == 'right') {
      if(toDot == 'top') {
        if(halfX < fromRightX) {
          points.push(fromRightX + ',' + fromY);
          points.push(fromRightX + ',' + toTopY);
          points.push(toX + ',' + toTopY);
        }
        else {
          points.push(halfX + ',' + fromY);
          points.push(halfX + ',' + toTopY);
          points.push(toX + ',' + toTopY);
        }
      }
      else if(toDot == 'right') {
        points.push(toRightX + ',' + fromY);
        points.push(toRightX + ',' + toY);
      }
      else if(toDot == 'bottom') {
        if(fromY < toBottomY) {
          if(halfX < fromRightX) {
            points.push(fromRightX + ',' + fromY);
            points.push(fromRightX + ',' + toBottomY);
            points.push(toX + ',' + toBottomY);
          }
          else {
            points.push(halfX + ',' + fromY);
            points.push(halfX + ',' + toBottomY);
            points.push(toX + ',' + toBottomY);
          }
        }
        else if(toX < fromRightX) {
          if(halfY < toBottomY) {
            points.push(fromRightX + ',' + fromY);
            points.push(fromRightX + ',' + toBottomY);
            points.push(toX + ',' + toBottomY);
            
          }
          else {
            points.push(fromRightX + ',' + fromY);
            points.push(fromRightX + ',' + halfY);
            points.push(toX + ',' + halfY);
          }
        }
        else {
          points.push(toX + ',' + fromY);
        }
      }
      else if(toDot == 'left') {
        if(toLeftX < fromRightX) {
          points.push(fromRightX + ',' + fromY);
          points.push(fromRightX + ',' + halfY);
          points.push(toLeftX + ',' + halfY);
          points.push(toLeftX + ',' + toY);
        }
        else {
          points.push(halfX + ',' + fromY);
          points.push(halfX + ',' + toY);
        }
      }
    }
    else if(fromDot == 'bottom') {
      if(toDot == 'top') {
        points.push(fromX + ',' + fromBottomY);
        points.push(halfX + ',' + fromBottomY);
        points.push(halfX + ',' + toTopY);
        points.push(toX + ',' + toTopY);
      }
      else if(toDot == 'right') {
        points.push(fromX + ',' + fromBottomY);
        points.push(toRightX + ',' + fromBottomY);
        points.push(toRightX + ',' + toY);
      }
      else if(toDot == 'bottom') {
        points.push(fromX + ',' + fromBottomY);
        points.push(toX + ',' + fromBottomY);
      }
      else if(toDot == 'left') {
        if(toLeftX < halfX) {
          points.push(fromX + ',' + fromBottomY);
          points.push(toLeftX + ',' + fromBottomY);
          points.push(toLeftX + ',' + toY);
        }
        else {
          points.push(fromX + ',' + fromBottomY);
          points.push(halfX + ',' + fromBottomY);
          points.push(halfX + ',' + toY);
        }
      }
    }
    else if(fromDot == 'left') {
      if(toDot == 'top') {
        points.push(fromLeftX + ',' + fromY);
        points.push(fromLeftX + ',' + halfY);
        points.push(fromLeftX + ',' + toTopY);
        points.push(toX + ',' + toTopY);
      }
      else if(toDot == 'right') {
        points.push(fromLeftX + ',' + fromY);
        points.push(fromLeftX + ',' + halfY);
        points.push(toRightX + ',' + halfY);
        points.push(toRightX + ',' + toY);
      }
      else if(toDot == 'bottom') {
        if(halfY < toBottomY) {
          points.push(fromLeftX + ',' + fromY);
          points.push(fromLeftX + ',' + toBottomY);
          points.push(toX + ',' + toBottomY);
        }
        else {
          points.push(fromLeftX + ',' + fromY);
          points.push(fromLeftX + ',' + halfY);
          points.push(toX + ',' + halfY);
        }
      }
      else if(toDot == 'left') {
        points.push(fromLeftX + ',' + fromY);
        points.push(fromLeftX + ',' + toY);
      }
    }
    return points;
  }
  This.$linePoints = (line, points) => {
    points = points || {};
    let lineType = line.type;
    let fromPoints = points.fromPoints || line.fromPoints;
    let toPoints = points.toPoints || line.toPoints;
    let linePath = function(t) {
      if(t == 'polyline') {
        let fromDot = line.fromDot;
        let toDot = line.toDot;
        let fromNodePos = line.fromNodePos;
        let toNodePos = line.toNodePos;
  
        let type = 'x';
        if(fromNodePos.x <= toNodePos.x && fromNodePos.y <= toNodePos.y) {
          type = 'a';
        }
        else if(fromNodePos.x >= toNodePos.x && fromNodePos.y >= toNodePos.y) {
          type = 'b';
        }
        else if(fromNodePos.x <= toNodePos.x && fromNodePos.y >= toNodePos.y) {
          type = 'c';
        }
        else if(fromNodePos.x >= toNodePos.x && fromNodePos.y <= toNodePos.y) {
          type = 'd';
        }
        let tempFromPoints = fromPoints;
        let tempToPoints = toPoints;
        let reverse = false;
        if(['b', 'd'].indexOf(type) != -1) {
          let tempDotType = fromDot;
          fromDot = toDot;
          toDot = tempDotType;
          tempFromPoints = toPoints;
          tempToPoints = fromPoints;
          reverse = true;
        }
        let fromX = parseFloat(tempFromPoints[0]);
        let fromY = parseFloat(tempFromPoints[1]);
        let toX = parseFloat(tempToPoints[0]);
        let toY = parseFloat(tempToPoints[1]);
        let minDotLen = This.$minDotLen;
        let fromTopY = fromY - minDotLen
        let fromRightX = fromX + minDotLen;
        let fromBottomY = fromY + minDotLen;
        let fromLeftX = fromX - minDotLen;
        let toTopY = toY - minDotLen;
        let toBottomY = toY + minDotLen;
        let toRightX = toX + minDotLen;
        let toLeftX = toX - minDotLen;
        let halfX = (fromX + toX) /2;
        let halfY = (fromY + toY) /2;
        let pointsArr = This[(['a', 'b'].indexOf(type) != -1)?'$lurbPoints':'$rulbPoints'](
              minDotLen,
              fromDot, toDot, tempFromPoints, tempToPoints,
              fromX, fromY, toX, toY,
              fromTopY, fromRightX, fromBottomY, fromLeftX,
              toTopY, toBottomY, toRightX, toLeftX,
              halfX, halfY
            );
        pointsArr = reverse ? pointsArr.reverse(): pointsArr;
        return pointsArr.join(' ').concat(' ');
      }
      else return '';
    }
    return fromPoints.join(',').concat(' ').concat(linePath(lineType)).concat(toPoints.join(','));
  }	

  
}, window)
